{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "<a href=\"https://colab.research.google.com/drive/1ZQG9Hnv1caDkcsmoCAf8_ynpqs3IQmST?usp=sharing\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"></a>"
      ],
      "metadata": {
        "id": "Fn6JYJipDdvM"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Self-Refine: Iterative Refinement with Self-Feedback"
      ],
      "metadata": {
        "id": "R97Nla-wQEZ_"
      }
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {
        "id": "kNRtRoOF5kAW"
      },
      "outputs": [],
      "source": [
        "!pip install -qU google-generativeai"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "import google.generativeai as genai\n",
        "import getpass"
      ],
      "metadata": {
        "id": "1mCzbywAQMAz"
      },
      "execution_count": 2,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Get free-tier Google's Gemini API Key here: https://aistudio.google.com/app/apikey"
      ],
      "metadata": {
        "id": "lKkJZmIUQOjt"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "API_KEY = getpass.getpass(\"Enter your Google API key: \")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "TyVQhfOUQQVz",
        "outputId": "79116b06-af42-4f40-96d5-8fd22b87d97d"
      },
      "execution_count": 8,
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Enter your Google API key: ··········\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "genai.configure(api_key=API_KEY)"
      ],
      "metadata": {
        "id": "I5OVdaKvQW-Q"
      },
      "execution_count": 11,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "class SelfRefineAgent:\n",
        "    def __init__(self):\n",
        "        self.model = genai.GenerativeModel(\"gemini-2.0-flash-exp\")\n",
        "        self.history = []\n",
        "\n",
        "    def generate(self, task):\n",
        "        \"\"\"Generate initial output\"\"\"\n",
        "        prompt = f\"\"\"Generate a response for this task:\n",
        "\n",
        "        Task: {task}\n",
        "\n",
        "        Response:\"\"\"\n",
        "\n",
        "        response = self.model.generate_content(prompt).text\n",
        "        return response.strip()\n",
        "\n",
        "    def critique(self, task, output):\n",
        "        \"\"\"Self-critique: Identify issues and areas for improvement\"\"\"\n",
        "        prompt = f\"\"\"Task: {task}\n",
        "\n",
        "        Current Output:\n",
        "        {output}\n",
        "\n",
        "        Critique this output. Identify:\n",
        "        1. What's good about it\n",
        "        2. What problems or errors exist\n",
        "        3. Specific improvements needed\n",
        "\n",
        "        Critique:\"\"\"\n",
        "\n",
        "        response = self.model.generate_content(prompt).text\n",
        "        return response.strip()\n",
        "\n",
        "    def refine(self, task, output, critique):\n",
        "        \"\"\"Refine output based on critique\"\"\"\n",
        "        prompt = f\"\"\"Task: {task}\n",
        "\n",
        "        Current Output:\n",
        "        {output}\n",
        "\n",
        "        Critique:\n",
        "        {critique}\n",
        "\n",
        "        Based on the critique, generate an improved version:\"\"\"\n",
        "\n",
        "        response = self.model.generate_content(prompt).text\n",
        "        return response.strip()\n",
        "\n",
        "    def evaluate_quality(self, task, output):\n",
        "        \"\"\"Evaluate if output meets quality standards (0-10)\"\"\"\n",
        "        prompt = f\"\"\"Task: {task}\n",
        "\n",
        "        Output:\n",
        "        {output}\n",
        "\n",
        "        Rate the quality of this output (0-10) based on:\n",
        "        - Correctness\n",
        "        - Completeness\n",
        "        - Clarity\n",
        "        - Relevance\n",
        "\n",
        "        Score (just number):\"\"\"\n",
        "\n",
        "        response = self.model.generate_content(prompt).text\n",
        "\n",
        "        try:\n",
        "            score = float(response.strip().split()[0])\n",
        "            return min(max(score, 0), 10)\n",
        "        except:\n",
        "            return 5.0\n",
        "\n",
        "    def self_refine(self, task, max_iterations=5, quality_threshold=8.0):\n",
        "        \"\"\"Main self-refinement loop\"\"\"\n",
        "        print(f\"\\n{'='*60}\")\n",
        "        print(f\"🔄 Self-Refine Loop\")\n",
        "        print(f\"{'='*60}\")\n",
        "        print(f\"Task: {task}\\n\")\n",
        "\n",
        "        # Step 1: Generate initial output\n",
        "        print(f\"{'─'*60}\")\n",
        "        print(f\"ITERATION 1: Initial Generation\")\n",
        "        print(f\"{'─'*60}\\n\")\n",
        "\n",
        "        output = self.generate(task)\n",
        "        quality = self.evaluate_quality(task, output)\n",
        "\n",
        "        print(f\"Generated Output:\\n{output}\\n\")\n",
        "        print(f\"Quality Score: {quality}/10\\n\")\n",
        "\n",
        "        self.history.append({\n",
        "            \"iteration\": 1,\n",
        "            \"output\": output,\n",
        "            \"quality\": quality,\n",
        "            \"critique\": None\n",
        "        })\n",
        "\n",
        "        # Refinement loop\n",
        "        for iteration in range(2, max_iterations + 1):\n",
        "            # Check if quality threshold met\n",
        "            if quality >= quality_threshold:\n",
        "                print(f\"✅ Quality threshold ({quality_threshold}) reached!\")\n",
        "                break\n",
        "\n",
        "            print(f\"{'─'*60}\")\n",
        "            print(f\"ITERATION {iteration}: Critique & Refine\")\n",
        "            print(f\"{'─'*60}\\n\")\n",
        "\n",
        "            # Step 2: Self-critique\n",
        "            print(\"🔍 Self-Critique:\")\n",
        "            critique = self.critique(task, output)\n",
        "            print(f\"{critique}\\n\")\n",
        "\n",
        "            # Step 3: Refine based on critique\n",
        "            print(\"✨ Refining output...\\n\")\n",
        "            output = self.refine(task, output, critique)\n",
        "\n",
        "            # Step 4: Evaluate new quality\n",
        "            quality = self.evaluate_quality(task, output)\n",
        "\n",
        "            print(f\"Refined Output:\\n{output}\\n\")\n",
        "            print(f\"Quality Score: {quality}/10\\n\")\n",
        "\n",
        "            self.history.append({\n",
        "                \"iteration\": iteration,\n",
        "                \"output\": output,\n",
        "                \"quality\": quality,\n",
        "                \"critique\": critique\n",
        "            })\n",
        "\n",
        "            # Check for diminishing returns\n",
        "            if iteration > 2:\n",
        "                prev_quality = self.history[-2][\"quality\"]\n",
        "                improvement = quality - prev_quality\n",
        "                if improvement < 0.5:\n",
        "                    print(f\"⚠️  Minimal improvement ({improvement:.1f}), stopping.\")\n",
        "                    break\n",
        "\n",
        "        # Show final result\n",
        "        print(f\"{'='*60}\")\n",
        "        print(f\"🏆 FINAL OUTPUT (Iteration {iteration})\")\n",
        "        print(f\"{'='*60}\")\n",
        "        print(output)\n",
        "        print(f\"\\nFinal Quality: {quality}/10\\n\")\n",
        "\n",
        "        self._show_history()\n",
        "\n",
        "        return output\n",
        "\n",
        "    def _show_history(self):\n",
        "        \"\"\"Display refinement history\"\"\"\n",
        "        print(f\"{'='*60}\")\n",
        "        print(f\"📊 REFINEMENT HISTORY\")\n",
        "        print(f\"{'='*60}\")\n",
        "\n",
        "        for entry in self.history:\n",
        "            print(f\"Iteration {entry['iteration']}: Quality {entry['quality']:.1f}/10\")\n",
        "\n",
        "        if len(self.history) > 1:\n",
        "            improvement = self.history[-1][\"quality\"] - self.history[0][\"quality\"]\n",
        "            print(f\"\\nTotal Improvement: +{improvement:.1f} points\")\n",
        "        print()"
      ],
      "metadata": {
        "id": "iqqAID3AQYrF"
      },
      "execution_count": 12,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# Example 1: Code Refinement\n",
        "print(\"=\"*60)\n",
        "print(\"EXAMPLE 1: Code Refinement\")\n",
        "print(\"=\"*60)\n",
        "\n",
        "agent1 = SelfRefineAgent()\n",
        "agent1.self_refine(\n",
        "    \"Write a Python function to find the factorial of a number\",\n",
        "    max_iterations=4,\n",
        "    quality_threshold=8.5\n",
        ")\n",
        "\n",
        "\n",
        "# Example 2: Writing Improvement\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "print(\"EXAMPLE 2: Writing Improvement\")\n",
        "print(\"=\"*60)\n",
        "\n",
        "agent2 = SelfRefineAgent()\n",
        "agent2.self_refine(\n",
        "    \"Write a professional email declining a job offer politely\",\n",
        "    max_iterations=4,\n",
        "    quality_threshold=8.0\n",
        ")\n",
        "\n",
        "\n",
        "# Example 3: Translation Polishing\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "print(\"EXAMPLE 3: Translation Polishing\")\n",
        "print(\"=\"*60)\n",
        "\n",
        "agent3 = SelfRefineAgent()\n",
        "agent3.self_refine(\n",
        "    \"Translate 'The early bird catches the worm' to Spanish, preserving the meaning and cultural relevance\",\n",
        "    max_iterations=3,\n",
        "    quality_threshold=8.5\n",
        ")\n",
        "\n",
        "\n",
        "# Example 4: Algorithm Enhancement\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "print(\"EXAMPLE 4: Algorithm Enhancement\")\n",
        "print(\"=\"*60)\n",
        "\n",
        "agent4 = SelfRefineAgent()\n",
        "agent4.self_refine(\n",
        "    \"Design an algorithm to find the shortest path in a weighted graph\",\n",
        "    max_iterations=4,\n",
        "    quality_threshold=8.0\n",
        ")\n",
        "\n",
        "\n",
        "# Example 5: Content Summarization\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "print(\"EXAMPLE 5: Content Summarization\")\n",
        "print(\"=\"*60)\n",
        "\n",
        "agent5 = SelfRefineAgent()\n",
        "agent5.self_refine(\n",
        "    \"Summarize the key benefits of remote work in 3 concise bullet points\",\n",
        "    max_iterations=3,\n",
        "    quality_threshold=8.5\n",
        ")\n",
        "\n",
        "\n",
        "# Example 6: Creative Writing\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "print(\"EXAMPLE 6: Creative Idea Iteration\")\n",
        "print(\"=\"*60)\n",
        "\n",
        "agent6 = SelfRefineAgent()\n",
        "agent6.self_refine(\n",
        "    \"Create a tagline for an eco-friendly water bottle brand\",\n",
        "    max_iterations=4,\n",
        "    quality_threshold=8.0\n",
        ")\n",
        "\n",
        "\n",
        "print(\"✅ Self-Refine Complete!\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        },
        "id": "ViD-nxhRQulw",
        "outputId": "f586ce7b-38c2-460f-dc33-f2825d7ff094"
      },
      "execution_count": 13,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "============================================================\n",
            "EXAMPLE 1: Code Refinement\n",
            "============================================================\n",
            "\n",
            "============================================================\n",
            "🔄 Self-Refine Loop\n",
            "============================================================\n",
            "Task: Write a Python function to find the factorial of a number\n",
            "\n",
            "────────────────────────────────────────────────────────────\n",
            "ITERATION 1: Initial Generation\n",
            "────────────────────────────────────────────────────────────\n",
            "\n",
            "Generated Output:\n",
            "```python\n",
            "def factorial(n):\n",
            "  \"\"\"\n",
            "  This function calculates the factorial of a non-negative integer.\n",
            "\n",
            "  Args:\n",
            "    n: A non-negative integer.\n",
            "\n",
            "  Returns:\n",
            "    The factorial of n (n!), or 1 if n is 0.\n",
            "    Returns None if n is negative.\n",
            "  \"\"\"\n",
            "  if n < 0:\n",
            "    return None  # Factorial is not defined for negative numbers\n",
            "  elif n == 0:\n",
            "    return 1\n",
            "  else:\n",
            "    result = 1\n",
            "    for i in range(1, n + 1):\n",
            "      result *= i\n",
            "    return result\n",
            "\n",
            "# Example Usage:\n",
            "if __name__ == '__main__':\n",
            "  print(factorial(5))  # Output: 120\n",
            "  print(factorial(0))  # Output: 1\n",
            "  print(factorial(-1)) # Output: None\n",
            "```\n",
            "\n",
            "**Explanation:**\n",
            "\n",
            "1. **Function Definition:**\n",
            "   - The code defines a function called `factorial(n)` that takes one argument `n`, representing the number for which we want to calculate the factorial.\n",
            "\n",
            "2. **Error Handling (Negative Numbers):**\n",
            "   - `if n < 0:`: This checks if the input number `n` is negative.  Factorials are not defined for negative numbers. If `n` is negative, the function returns `None` to indicate an error.\n",
            "\n",
            "3. **Base Case (0!):**\n",
            "   - `elif n == 0:`: This handles the base case for the recursion or iteration.  The factorial of 0 (0!) is defined as 1.  So, if `n` is 0, the function returns 1.\n",
            "\n",
            "4. **Iterative Calculation:**\n",
            "   - `else:`: If `n` is a positive integer, this block executes.\n",
            "   - `result = 1`:  A variable `result` is initialized to 1. This will store the factorial value.\n",
            "   - `for i in range(1, n + 1):`:  A `for` loop iterates from 1 up to `n` (inclusive).  The `range(1, n + 1)` creates a sequence of numbers: 1, 2, 3, ..., n.\n",
            "   - `result *= i`:  In each iteration, the current value of `result` is multiplied by `i`.  This effectively calculates the product 1 * 2 * 3 * ... * n, which is the factorial of n.\n",
            "   - `return result`: After the loop finishes, the function returns the calculated `result`, which is the factorial of `n`.\n",
            "\n",
            "5. **Example Usage (in `if __name__ == '__main__':`)**\n",
            "   -  The `if __name__ == '__main__':` block ensures that the example code only runs when the script is executed directly (not when it's imported as a module).\n",
            "   -  It calls the `factorial()` function with different input values (5, 0, -1) and prints the returned results to the console, demonstrating how to use the function and showing the expected outputs.\n",
            "\n",
            "Quality Score: 10.0/10\n",
            "\n",
            "✅ Quality threshold (8.5) reached!\n",
            "============================================================\n",
            "🏆 FINAL OUTPUT (Iteration 2)\n",
            "============================================================\n",
            "```python\n",
            "def factorial(n):\n",
            "  \"\"\"\n",
            "  This function calculates the factorial of a non-negative integer.\n",
            "\n",
            "  Args:\n",
            "    n: A non-negative integer.\n",
            "\n",
            "  Returns:\n",
            "    The factorial of n (n!), or 1 if n is 0.\n",
            "    Returns None if n is negative.\n",
            "  \"\"\"\n",
            "  if n < 0:\n",
            "    return None  # Factorial is not defined for negative numbers\n",
            "  elif n == 0:\n",
            "    return 1\n",
            "  else:\n",
            "    result = 1\n",
            "    for i in range(1, n + 1):\n",
            "      result *= i\n",
            "    return result\n",
            "\n",
            "# Example Usage:\n",
            "if __name__ == '__main__':\n",
            "  print(factorial(5))  # Output: 120\n",
            "  print(factorial(0))  # Output: 1\n",
            "  print(factorial(-1)) # Output: None\n",
            "```\n",
            "\n",
            "**Explanation:**\n",
            "\n",
            "1. **Function Definition:**\n",
            "   - The code defines a function called `factorial(n)` that takes one argument `n`, representing the number for which we want to calculate the factorial.\n",
            "\n",
            "2. **Error Handling (Negative Numbers):**\n",
            "   - `if n < 0:`: This checks if the input number `n` is negative.  Factorials are not defined for negative numbers. If `n` is negative, the function returns `None` to indicate an error.\n",
            "\n",
            "3. **Base Case (0!):**\n",
            "   - `elif n == 0:`: This handles the base case for the recursion or iteration.  The factorial of 0 (0!) is defined as 1.  So, if `n` is 0, the function returns 1.\n",
            "\n",
            "4. **Iterative Calculation:**\n",
            "   - `else:`: If `n` is a positive integer, this block executes.\n",
            "   - `result = 1`:  A variable `result` is initialized to 1. This will store the factorial value.\n",
            "   - `for i in range(1, n + 1):`:  A `for` loop iterates from 1 up to `n` (inclusive).  The `range(1, n + 1)` creates a sequence of numbers: 1, 2, 3, ..., n.\n",
            "   - `result *= i`:  In each iteration, the current value of `result` is multiplied by `i`.  This effectively calculates the product 1 * 2 * 3 * ... * n, which is the factorial of n.\n",
            "   - `return result`: After the loop finishes, the function returns the calculated `result`, which is the factorial of `n`.\n",
            "\n",
            "5. **Example Usage (in `if __name__ == '__main__':`)**\n",
            "   -  The `if __name__ == '__main__':` block ensures that the example code only runs when the script is executed directly (not when it's imported as a module).\n",
            "   -  It calls the `factorial()` function with different input values (5, 0, -1) and prints the returned results to the console, demonstrating how to use the function and showing the expected outputs.\n",
            "\n",
            "Final Quality: 10.0/10\n",
            "\n",
            "============================================================\n",
            "📊 REFINEMENT HISTORY\n",
            "============================================================\n",
            "Iteration 1: Quality 10.0/10\n",
            "\n",
            "\n",
            "============================================================\n",
            "EXAMPLE 2: Writing Improvement\n",
            "============================================================\n",
            "\n",
            "============================================================\n",
            "🔄 Self-Refine Loop\n",
            "============================================================\n",
            "Task: Write a professional email declining a job offer politely\n",
            "\n",
            "────────────────────────────────────────────────────────────\n",
            "ITERATION 1: Initial Generation\n",
            "────────────────────────────────────────────────────────────\n",
            "\n",
            "Generated Output:\n",
            "Okay, here's a template for a professional email declining a job offer politely, along with some variations to suit different situations:\n",
            "\n",
            "**Subject: Job Offer - [Your Name] - [Job Title]**\n",
            "\n",
            "**Option 1: Standard and General**\n",
            "\n",
            "Dear [Hiring Manager Name],\n",
            "\n",
            "Thank you so much for offering me the position of [Job Title] at [Company Name]. I truly appreciate you and the team taking the time to interview me and share more about this opportunity.\n",
            "\n",
            "After careful consideration, I have decided to decline the offer at this time. This was a difficult decision, as I was very impressed with [Company Name] and the team. However, I have accepted a position that is a better fit for my career goals and current circumstances.\n",
            "\n",
            "Thank you again for your time and consideration. I wish you and [Company Name] all the best in your search for a suitable candidate.\n",
            "\n",
            "Sincerely,\n",
            "\n",
            "[Your Name]\n",
            "\n",
            "**Option 2: More Specific (If you have a reason you're comfortable sharing)**\n",
            "\n",
            "Dear [Hiring Manager Name],\n",
            "\n",
            "Thank you very much for offering me the position of [Job Title] at [Company Name]. I greatly appreciate you and the team taking the time to interview me and introduce me to [Company Name].\n",
            "\n",
            "After careful consideration, I have decided to decline the offer at this time. I was particularly impressed with [Mention something specific you liked - e.g., the company culture, the team's enthusiasm, a particular project discussed]. However, I have accepted a position that [briefly explain the reason without going into too much detail - e.g., aligns more closely with my long-term career aspirations, offers a different set of challenges, is geographically more suitable].\n",
            "\n",
            "Thank you again for your time and consideration. I wish you and [Company Name] the best in finding a qualified candidate for this role.\n",
            "\n",
            "Sincerely,\n",
            "\n",
            "[Your Name]\n",
            "\n",
            "**Option 3: Expressing Interest in Future Opportunities**\n",
            "\n",
            "Dear [Hiring Manager Name],\n",
            "\n",
            "Thank you so much for offering me the position of [Job Title] at [Company Name]. I truly appreciate the time you and the team invested in interviewing me and sharing more about this exciting opportunity.\n",
            "\n",
            "After careful consideration, I have decided to decline the offer at this time. I was very impressed with [Company Name] and the team, and I am grateful for the opportunity to have learned more about your organization.\n",
            "\n",
            "While this particular role isn't the right fit for me at this moment, I would be very interested in exploring potential opportunities with [Company Name] in the future.\n",
            "\n",
            "Thank you again for your time and consideration. I wish you and [Company Name] all the best in your search.\n",
            "\n",
            "Sincerely,\n",
            "\n",
            "[Your Name]\n",
            "\n",
            "**Option 4: If you received other offers, but don't want to be too explicit**\n",
            "\n",
            "Dear [Hiring Manager Name],\n",
            "\n",
            "Thank you so much for offering me the position of [Job Title] at [Company Name]. I sincerely appreciate the time you and the team took to interview me and share more about the role.\n",
            "\n",
            "After careful consideration, I have decided to decline the offer at this time.  This was a difficult decision as I was genuinely impressed with [Company Name]. Ultimately, I've decided to pursue a different opportunity that aligns more closely with my current career path.\n",
            "\n",
            "Thank you again for your time and consideration.  I wish you and [Company Name] the very best in finding a suitable candidate.\n",
            "\n",
            "Sincerely,\n",
            "\n",
            "[Your Name]\n",
            "\n",
            "**Key Considerations and Customization:**\n",
            "\n",
            "*   **Always be prompt:** Respond as soon as you've made your decision.\n",
            "*   **Be sincere:**  Express genuine gratitude for the offer and their time.\n",
            "*   **Be clear:** State your decision clearly and unambiguously.\n",
            "*   **Be concise:** Keep the email brief and to the point.\n",
            "*   **Be professional:** Maintain a polite and respectful tone throughout.\n",
            "*   **Mention Something Positive:** If possible, mention something specific you liked about the company or the opportunity. This leaves a good impression.\n",
            "*   **Avoid Negativity:**  Do not mention anything negative about the company, the job, or the people you met.\n",
            "*   **Proofread:** Always proofread your email carefully before sending it.\n",
            "\n",
            "**When to Use Which Option:**\n",
            "\n",
            "*   **Option 1 (Standard):**  Use this when you want a simple, polite decline without giving specific reasons.  It's the safest choice.\n",
            "*   **Option 2 (More Specific):** Use this if you feel comfortable sharing a *brief* and *positive* reason for your decision.  For example, if you're taking a role closer to home.\n",
            "*   **Option 3 (Future Opportunities):**  Use this *only* if you are genuinely interested in working for the company in the future. Don't say this if you don't mean it.\n",
            "*   **Option 4 (Other Offers):** Use this when you want to subtly indicate you had other offers, without explicitly stating it.\n",
            "\n",
            "**Important Note:**\n",
            "\n",
            "*   **Don't negotiate:**  Unless you're open to accepting the offer if certain conditions are met, don't start a negotiation at this point.  It's better to be clear that you're declining.\n",
            "*   **Consider your industry:**  In some industries, it's more common to be direct and less flowery with your language. Tailor the tone to what's appropriate for your field.\n",
            "*   **Burning Bridges:** This email is designed to *avoid* burning bridges.  Always err on the side of politeness and professionalism.\n",
            "\n",
            "Remember to replace the bracketed placeholders with the appropriate information. Good luck!\n",
            "\n",
            "Quality Score: 10.0/10\n",
            "\n",
            "✅ Quality threshold (8.0) reached!\n",
            "============================================================\n",
            "🏆 FINAL OUTPUT (Iteration 2)\n",
            "============================================================\n",
            "Okay, here's a template for a professional email declining a job offer politely, along with some variations to suit different situations:\n",
            "\n",
            "**Subject: Job Offer - [Your Name] - [Job Title]**\n",
            "\n",
            "**Option 1: Standard and General**\n",
            "\n",
            "Dear [Hiring Manager Name],\n",
            "\n",
            "Thank you so much for offering me the position of [Job Title] at [Company Name]. I truly appreciate you and the team taking the time to interview me and share more about this opportunity.\n",
            "\n",
            "After careful consideration, I have decided to decline the offer at this time. This was a difficult decision, as I was very impressed with [Company Name] and the team. However, I have accepted a position that is a better fit for my career goals and current circumstances.\n",
            "\n",
            "Thank you again for your time and consideration. I wish you and [Company Name] all the best in your search for a suitable candidate.\n",
            "\n",
            "Sincerely,\n",
            "\n",
            "[Your Name]\n",
            "\n",
            "**Option 2: More Specific (If you have a reason you're comfortable sharing)**\n",
            "\n",
            "Dear [Hiring Manager Name],\n",
            "\n",
            "Thank you very much for offering me the position of [Job Title] at [Company Name]. I greatly appreciate you and the team taking the time to interview me and introduce me to [Company Name].\n",
            "\n",
            "After careful consideration, I have decided to decline the offer at this time. I was particularly impressed with [Mention something specific you liked - e.g., the company culture, the team's enthusiasm, a particular project discussed]. However, I have accepted a position that [briefly explain the reason without going into too much detail - e.g., aligns more closely with my long-term career aspirations, offers a different set of challenges, is geographically more suitable].\n",
            "\n",
            "Thank you again for your time and consideration. I wish you and [Company Name] the best in finding a qualified candidate for this role.\n",
            "\n",
            "Sincerely,\n",
            "\n",
            "[Your Name]\n",
            "\n",
            "**Option 3: Expressing Interest in Future Opportunities**\n",
            "\n",
            "Dear [Hiring Manager Name],\n",
            "\n",
            "Thank you so much for offering me the position of [Job Title] at [Company Name]. I truly appreciate the time you and the team invested in interviewing me and sharing more about this exciting opportunity.\n",
            "\n",
            "After careful consideration, I have decided to decline the offer at this time. I was very impressed with [Company Name] and the team, and I am grateful for the opportunity to have learned more about your organization.\n",
            "\n",
            "While this particular role isn't the right fit for me at this moment, I would be very interested in exploring potential opportunities with [Company Name] in the future.\n",
            "\n",
            "Thank you again for your time and consideration. I wish you and [Company Name] all the best in your search.\n",
            "\n",
            "Sincerely,\n",
            "\n",
            "[Your Name]\n",
            "\n",
            "**Option 4: If you received other offers, but don't want to be too explicit**\n",
            "\n",
            "Dear [Hiring Manager Name],\n",
            "\n",
            "Thank you so much for offering me the position of [Job Title] at [Company Name]. I sincerely appreciate the time you and the team took to interview me and share more about the role.\n",
            "\n",
            "After careful consideration, I have decided to decline the offer at this time.  This was a difficult decision as I was genuinely impressed with [Company Name]. Ultimately, I've decided to pursue a different opportunity that aligns more closely with my current career path.\n",
            "\n",
            "Thank you again for your time and consideration.  I wish you and [Company Name] the very best in finding a suitable candidate.\n",
            "\n",
            "Sincerely,\n",
            "\n",
            "[Your Name]\n",
            "\n",
            "**Key Considerations and Customization:**\n",
            "\n",
            "*   **Always be prompt:** Respond as soon as you've made your decision.\n",
            "*   **Be sincere:**  Express genuine gratitude for the offer and their time.\n",
            "*   **Be clear:** State your decision clearly and unambiguously.\n",
            "*   **Be concise:** Keep the email brief and to the point.\n",
            "*   **Be professional:** Maintain a polite and respectful tone throughout.\n",
            "*   **Mention Something Positive:** If possible, mention something specific you liked about the company or the opportunity. This leaves a good impression.\n",
            "*   **Avoid Negativity:**  Do not mention anything negative about the company, the job, or the people you met.\n",
            "*   **Proofread:** Always proofread your email carefully before sending it.\n",
            "\n",
            "**When to Use Which Option:**\n",
            "\n",
            "*   **Option 1 (Standard):**  Use this when you want a simple, polite decline without giving specific reasons.  It's the safest choice.\n",
            "*   **Option 2 (More Specific):** Use this if you feel comfortable sharing a *brief* and *positive* reason for your decision.  For example, if you're taking a role closer to home.\n",
            "*   **Option 3 (Future Opportunities):**  Use this *only* if you are genuinely interested in working for the company in the future. Don't say this if you don't mean it.\n",
            "*   **Option 4 (Other Offers):** Use this when you want to subtly indicate you had other offers, without explicitly stating it.\n",
            "\n",
            "**Important Note:**\n",
            "\n",
            "*   **Don't negotiate:**  Unless you're open to accepting the offer if certain conditions are met, don't start a negotiation at this point.  It's better to be clear that you're declining.\n",
            "*   **Consider your industry:**  In some industries, it's more common to be direct and less flowery with your language. Tailor the tone to what's appropriate for your field.\n",
            "*   **Burning Bridges:** This email is designed to *avoid* burning bridges.  Always err on the side of politeness and professionalism.\n",
            "\n",
            "Remember to replace the bracketed placeholders with the appropriate information. Good luck!\n",
            "\n",
            "Final Quality: 10.0/10\n",
            "\n",
            "============================================================\n",
            "📊 REFINEMENT HISTORY\n",
            "============================================================\n",
            "Iteration 1: Quality 10.0/10\n",
            "\n",
            "\n",
            "============================================================\n",
            "EXAMPLE 3: Translation Polishing\n",
            "============================================================\n",
            "\n",
            "============================================================\n",
            "🔄 Self-Refine Loop\n",
            "============================================================\n",
            "Task: Translate 'The early bird catches the worm' to Spanish, preserving the meaning and cultural relevance\n",
            "\n",
            "────────────────────────────────────────────────────────────\n",
            "ITERATION 1: Initial Generation\n",
            "────────────────────────────────────────────────────────────\n",
            "\n",
            "Generated Output:\n",
            "There are several good translations of \"The early bird catches the worm\" into Spanish, each with slightly different nuances. Here are a few options, along with explanations to help you choose the best fit for your context:\n",
            "\n",
            "*   **\"A quien madruga, Dios le ayuda.\"** This is the most common and arguably the *best* translation. It literally translates to \"God helps those who wake up early.\" It's widely understood and carries the same cultural weight as the English proverb, emphasizing the benefits of being proactive and starting early. It's religiously-tinged, but commonly used regardless of religious beliefs.\n",
            "\n",
            "*   **\"El que madruga, Dios le ayuda.\"** This is a very similar variant of the above, just adding the article \"el\" before \"que\". It translates to \"He who wakes up early, God helps him.\" Equally good and common as the previous one.\n",
            "\n",
            "*   **\"El que madruga coge/trinca la liebre.\"** (He who wakes up early catches the hare/rabbit.) This version replaces the \"worm\" with a \"hare/rabbit,\" making it arguably more relevant in some cultures where worms might not be a common part of the local fauna's diet. \"Coge\" is more common than \"trinca\", but both work. This has a slightly more hunting-related connotation.\n",
            "\n",
            "**Why these options are good:**\n",
            "\n",
            "*   **Meaning Preservation:** They all convey the core message of the original proverb: early action leads to reward.\n",
            "*   **Cultural Relevance:** They are common and well-understood sayings within Spanish-speaking cultures. They feel natural and not like a direct, awkward translation.\n",
            "*   **Figurative Language:** They maintain the use of figurative language, which is essential to the proverb's impact.\n",
            "\n",
            "**Which one to choose?**\n",
            "\n",
            "*   For general use and the closest equivalent in terms of cultural understanding, **\"A quien madruga, Dios le ayuda\"** or **\"El que madruga, Dios le ayuda\"** are the best choices.\n",
            "*   If you want a version that replaces the worm with something more common, **\"El que madruga coge/trinca la liebre\"** is a good option, but be aware it shifts the context to more of a hunting scenario.\n",
            "\n",
            "Therefore, my primary recommendation is:\n",
            "\n",
            "**\"A quien madruga, Dios le ayuda.\"**\n",
            "\n",
            "Quality Score: 10.0/10\n",
            "\n",
            "✅ Quality threshold (8.5) reached!\n",
            "============================================================\n",
            "🏆 FINAL OUTPUT (Iteration 2)\n",
            "============================================================\n",
            "There are several good translations of \"The early bird catches the worm\" into Spanish, each with slightly different nuances. Here are a few options, along with explanations to help you choose the best fit for your context:\n",
            "\n",
            "*   **\"A quien madruga, Dios le ayuda.\"** This is the most common and arguably the *best* translation. It literally translates to \"God helps those who wake up early.\" It's widely understood and carries the same cultural weight as the English proverb, emphasizing the benefits of being proactive and starting early. It's religiously-tinged, but commonly used regardless of religious beliefs.\n",
            "\n",
            "*   **\"El que madruga, Dios le ayuda.\"** This is a very similar variant of the above, just adding the article \"el\" before \"que\". It translates to \"He who wakes up early, God helps him.\" Equally good and common as the previous one.\n",
            "\n",
            "*   **\"El que madruga coge/trinca la liebre.\"** (He who wakes up early catches the hare/rabbit.) This version replaces the \"worm\" with a \"hare/rabbit,\" making it arguably more relevant in some cultures where worms might not be a common part of the local fauna's diet. \"Coge\" is more common than \"trinca\", but both work. This has a slightly more hunting-related connotation.\n",
            "\n",
            "**Why these options are good:**\n",
            "\n",
            "*   **Meaning Preservation:** They all convey the core message of the original proverb: early action leads to reward.\n",
            "*   **Cultural Relevance:** They are common and well-understood sayings within Spanish-speaking cultures. They feel natural and not like a direct, awkward translation.\n",
            "*   **Figurative Language:** They maintain the use of figurative language, which is essential to the proverb's impact.\n",
            "\n",
            "**Which one to choose?**\n",
            "\n",
            "*   For general use and the closest equivalent in terms of cultural understanding, **\"A quien madruga, Dios le ayuda\"** or **\"El que madruga, Dios le ayuda\"** are the best choices.\n",
            "*   If you want a version that replaces the worm with something more common, **\"El que madruga coge/trinca la liebre\"** is a good option, but be aware it shifts the context to more of a hunting scenario.\n",
            "\n",
            "Therefore, my primary recommendation is:\n",
            "\n",
            "**\"A quien madruga, Dios le ayuda.\"**\n",
            "\n",
            "Final Quality: 10.0/10\n",
            "\n",
            "============================================================\n",
            "📊 REFINEMENT HISTORY\n",
            "============================================================\n",
            "Iteration 1: Quality 10.0/10\n",
            "\n",
            "\n",
            "============================================================\n",
            "EXAMPLE 4: Algorithm Enhancement\n",
            "============================================================\n",
            "\n",
            "============================================================\n",
            "🔄 Self-Refine Loop\n",
            "============================================================\n",
            "Task: Design an algorithm to find the shortest path in a weighted graph\n",
            "\n",
            "────────────────────────────────────────────────────────────\n",
            "ITERATION 1: Initial Generation\n",
            "────────────────────────────────────────────────────────────\n",
            "\n",
            "Generated Output:\n",
            "Okay, here's a breakdown of an algorithm for finding the shortest path in a weighted graph.  I'll focus on Dijkstra's algorithm as it's a common and well-understood solution, especially for graphs with non-negative edge weights.  I'll also touch on the Bellman-Ford algorithm which handles negative edge weights.\n",
            "\n",
            "**1. Algorithm: Dijkstra's Algorithm**\n",
            "\n",
            "Dijkstra's algorithm finds the shortest paths from a single source node to all other nodes in a graph with non-negative edge weights.\n",
            "\n",
            "**1.1 Input:**\n",
            "\n",
            "*   `Graph G`:  A weighted graph represented as an adjacency list or adjacency matrix.  Each edge has a non-negative weight.\n",
            "*   `Source Node s`: The starting node for finding the shortest paths.\n",
            "\n",
            "**1.2 Data Structures:**\n",
            "\n",
            "*   `dist[v]`: An array (or dictionary) to store the shortest distance from the source node `s` to each vertex `v`. Initially, set all `dist[v]` to infinity (or a very large number) except for `dist[s]`, which is set to 0.\n",
            "*   `visited[v]`: An array (or dictionary) to keep track of visited vertices. Initially, all `visited[v]` are false.\n",
            "*   `priority_queue Q`: A priority queue (min-heap) that stores vertices prioritized by their current shortest distance from the source.  We will enqueue (vertex, distance) pairs into Q.\n",
            "\n",
            "**1.3 Algorithm Steps:**\n",
            "\n",
            "1.  **Initialization:**\n",
            "    *   Set `dist[s] = 0` (distance from source to itself is 0).\n",
            "    *   For all other vertices `v` (where `v != s`), set `dist[v] = infinity`.\n",
            "    *   For all vertices `v`, set `visited[v] = false`.\n",
            "    *   Enqueue `(s, 0)` into the priority queue `Q`. (Vertex `s` with distance 0).\n",
            "\n",
            "2.  **Main Loop:**\n",
            "    *   While the priority queue `Q` is not empty:\n",
            "        *   Extract the vertex `u` with the smallest distance from `Q`.  Let `d` be the distance associated with `u`.\n",
            "        *   If `d > dist[u]`, continue to the next iteration (this avoids processing stale entries in the priority queue).\n",
            "\n",
            "        *   If `visited[u]` is true, continue to the next iteration. (already processed this node with shortest distance)\n",
            "        *   Set `visited[u] = true`.  Mark `u` as visited.\n",
            "\n",
            "        *   For each neighbor `v` of vertex `u` (i.e., for each edge `(u, v)` in the graph):\n",
            "            *   Let `weight(u, v)` be the weight of the edge between `u` and `v`.\n",
            "            *   If `dist[u] + weight(u, v) < dist[v]` :\n",
            "                *   `dist[v] = dist[u] + weight(u, v)` (Found a shorter path to `v`)\n",
            "                *   Enqueue `(v, dist[v])` into the priority queue `Q`.\n",
            "\n",
            "3.  **Result:**\n",
            "    *   After the loop finishes, the `dist[v]` array contains the shortest distance from the source node `s` to each vertex `v` in the graph.  If `dist[v]` is still infinity for some vertex `v`, it means there is no path from `s` to `v`.\n",
            "\n",
            "**1.4 Example (Adjacency List Representation):**\n",
            "\n",
            "```python\n",
            "import heapq  # For priority queue implementation\n",
            "\n",
            "def dijkstra(graph, start):\n",
            "    distances = {node: float('inf') for node in graph}\n",
            "    distances[start] = 0\n",
            "    visited = {node: False for node in graph}\n",
            "    pq = [(0, start)]  # (distance, node)\n",
            "\n",
            "    while pq:\n",
            "        dist, u = heapq.heappop(pq)\n",
            "\n",
            "        if dist > distances[u]: # handle duplicates in priority queue\n",
            "          continue\n",
            "\n",
            "        if visited[u]:\n",
            "            continue\n",
            "        visited[u] = True\n",
            "\n",
            "        for v, weight in graph[u].items():  # Iterate through neighbors of u\n",
            "            if distances[u] + weight < distances[v]:\n",
            "                distances[v] = distances[u] + weight\n",
            "                heapq.heappush(pq, (distances[v], v))\n",
            "\n",
            "    return distances\n",
            "\n",
            "# Example Graph (Adjacency List)\n",
            "graph = {\n",
            "    'A': {'B': 4, 'C': 2},\n",
            "    'B': {'A': 4, 'D': 5},\n",
            "    'C': {'A': 2, 'D': 1, 'E': 3},\n",
            "    'D': {'B': 5, 'C': 1, 'E': 1},\n",
            "    'E': {'C': 3, 'D': 1}\n",
            "}\n",
            "\n",
            "start_node = 'A'\n",
            "shortest_distances = dijkstra(graph, start_node)\n",
            "\n",
            "print(f\"Shortest distances from {start_node}: {shortest_distances}\")\n",
            "```\n",
            "\n",
            "**1.5 Time Complexity:**\n",
            "\n",
            "*   Using a binary heap as the priority queue: O((|E| + |V|) log |V|) which simplifies to O(|E| log |V|) in a connected graph.  |V| is the number of vertices, and |E| is the number of edges.\n",
            "*   Using a Fibonacci heap as the priority queue: O(|E| + |V| log |V|)  (Better asymptotic complexity, but more complex to implement)\n",
            "\n",
            "**1.6 Space Complexity:** O(|V|)  (For `dist`, `visited`, and the priority queue).\n",
            "\n",
            "**2. Algorithm: Bellman-Ford Algorithm**\n",
            "\n",
            "Bellman-Ford algorithm finds the shortest paths from a single source node to all other nodes in a *directed* graph, even if the graph contains negative edge weights.  Crucially, it can detect the presence of *negative weight cycles*, which would make finding shortest paths impossible (you could continuously loop through the cycle to decrease the path length indefinitely).\n",
            "\n",
            "**2.1 Input:**\n",
            "\n",
            "*   `Graph G`: A directed weighted graph represented as a list of edges. Each edge has a weight (can be negative).  Edges are represented as (source_node, destination_node, weight).\n",
            "*   `Source Node s`: The starting node for finding the shortest paths.\n",
            "\n",
            "**2.2 Data Structures:**\n",
            "\n",
            "*   `dist[v]`: An array (or dictionary) to store the shortest distance from the source node `s` to each vertex `v`. Initially, set all `dist[v]` to infinity (or a very large number) except for `dist[s]`, which is set to 0.\n",
            "\n",
            "**2.3 Algorithm Steps:**\n",
            "\n",
            "1.  **Initialization:**\n",
            "    *   Set `dist[s] = 0`.\n",
            "    *   For all other vertices `v` (where `v != s`), set `dist[v] = infinity`.\n",
            "\n",
            "2.  **Relaxation:**\n",
            "    *   Repeat the following step |V| - 1 times (where |V| is the number of vertices):\n",
            "        *   For each edge (u, v, weight) in the graph:\n",
            "            *   If `dist[u] + weight < dist[v]` :\n",
            "                *   `dist[v] = dist[u] + weight`\n",
            "\n",
            "3.  **Negative Cycle Detection:**\n",
            "    *   For each edge (u, v, weight) in the graph:\n",
            "        *   If `dist[u] + weight < dist[v]` :\n",
            "            *   There is a negative weight cycle reachable from the source node.  The algorithm cannot find correct shortest paths.  Return an error or indicate that a negative cycle exists.\n",
            "\n",
            "4.  **Result:**\n",
            "    *   If no negative cycle is detected, the `dist[v]` array contains the shortest distances from the source node `s` to each vertex `v` in the graph.\n",
            "\n",
            "**2.4 Example:**\n",
            "\n",
            "```python\n",
            "def bellman_ford(edges, num_vertices, source):\n",
            "    distances = {i: float('inf') for i in range(num_vertices)}\n",
            "    distances[source] = 0\n",
            "\n",
            "    # Relaxation step (V-1 times)\n",
            "    for _ in range(num_vertices - 1):\n",
            "        for u, v, weight in edges:\n",
            "            if distances[u] != float('inf') and distances[u] + weight < distances[v]:\n",
            "                distances[v] = distances[u] + weight\n",
            "\n",
            "    # Negative cycle detection\n",
            "    for u, v, weight in edges:\n",
            "        if distances[u] != float('inf') and distances[u] + weight < distances[v]:\n",
            "            print(\"Graph contains negative weight cycle\")\n",
            "            return None  # Indicate negative cycle\n",
            "\n",
            "    return distances\n",
            "\n",
            "# Example graph with edges as (source, destination, weight)\n",
            "edges = [\n",
            "    (0, 1, -1),\n",
            "    (0, 2, 4),\n",
            "    (1, 2, 3),\n",
            "    (1, 3, 2),\n",
            "    (1, 4, 2),\n",
            "    (3, 2, 5),\n",
            "    (3, 1, 1),\n",
            "    (4, 3, -3)\n",
            "]\n",
            "\n",
            "num_vertices = 5\n",
            "source_node = 0\n",
            "shortest_distances = bellman_ford(edges, num_vertices, source_node)\n",
            "\n",
            "if shortest_distances:\n",
            "    print(f\"Shortest distances from node {source_node}: {shortest_distances}\")\n",
            "```\n",
            "\n",
            "**2.5 Time Complexity:** O(|V| * |E|)\n",
            "\n",
            "**2.6 Space Complexity:** O(|V|)\n",
            "\n",
            "**3. Choosing the Right Algorithm**\n",
            "\n",
            "*   **Dijkstra's:** Use when you have a graph with non-negative edge weights. It's generally faster than Bellman-Ford.\n",
            "*   **Bellman-Ford:** Use when your graph might contain negative edge weights, and you need to detect negative weight cycles.\n",
            "\n",
            "**4. Considerations and Optimizations**\n",
            "\n",
            "*   **Graph Representation:** The choice of adjacency list or adjacency matrix can impact performance, particularly for sparse graphs (adjacency lists are usually better).\n",
            "*   **Priority Queue Implementation:** The priority queue is a crucial component of Dijkstra's.  Using a good implementation (e.g., a binary heap or Fibonacci heap) can significantly improve performance.\n",
            "*   **Directed vs. Undirected Graphs:** Dijkstra's works for both, but Bellman-Ford is designed for directed graphs (although it can be applied to undirected graphs by representing each undirected edge as two directed edges).\n",
            "*   **All-Pairs Shortest Paths:** If you need to find the shortest paths between *all* pairs of vertices, algorithms like Floyd-Warshall may be more efficient than running Dijkstra's or Bellman-Ford multiple times.\n",
            "\n",
            "This detailed explanation and code examples should give you a good foundation for implementing and understanding shortest path algorithms. Remember to consider the specific characteristics of your graph (edge weights, directed/undirected) when choosing the appropriate algorithm.\n",
            "\n",
            "Quality Score: 10.0/10\n",
            "\n",
            "✅ Quality threshold (8.0) reached!\n",
            "============================================================\n",
            "🏆 FINAL OUTPUT (Iteration 2)\n",
            "============================================================\n",
            "Okay, here's a breakdown of an algorithm for finding the shortest path in a weighted graph.  I'll focus on Dijkstra's algorithm as it's a common and well-understood solution, especially for graphs with non-negative edge weights.  I'll also touch on the Bellman-Ford algorithm which handles negative edge weights.\n",
            "\n",
            "**1. Algorithm: Dijkstra's Algorithm**\n",
            "\n",
            "Dijkstra's algorithm finds the shortest paths from a single source node to all other nodes in a graph with non-negative edge weights.\n",
            "\n",
            "**1.1 Input:**\n",
            "\n",
            "*   `Graph G`:  A weighted graph represented as an adjacency list or adjacency matrix.  Each edge has a non-negative weight.\n",
            "*   `Source Node s`: The starting node for finding the shortest paths.\n",
            "\n",
            "**1.2 Data Structures:**\n",
            "\n",
            "*   `dist[v]`: An array (or dictionary) to store the shortest distance from the source node `s` to each vertex `v`. Initially, set all `dist[v]` to infinity (or a very large number) except for `dist[s]`, which is set to 0.\n",
            "*   `visited[v]`: An array (or dictionary) to keep track of visited vertices. Initially, all `visited[v]` are false.\n",
            "*   `priority_queue Q`: A priority queue (min-heap) that stores vertices prioritized by their current shortest distance from the source.  We will enqueue (vertex, distance) pairs into Q.\n",
            "\n",
            "**1.3 Algorithm Steps:**\n",
            "\n",
            "1.  **Initialization:**\n",
            "    *   Set `dist[s] = 0` (distance from source to itself is 0).\n",
            "    *   For all other vertices `v` (where `v != s`), set `dist[v] = infinity`.\n",
            "    *   For all vertices `v`, set `visited[v] = false`.\n",
            "    *   Enqueue `(s, 0)` into the priority queue `Q`. (Vertex `s` with distance 0).\n",
            "\n",
            "2.  **Main Loop:**\n",
            "    *   While the priority queue `Q` is not empty:\n",
            "        *   Extract the vertex `u` with the smallest distance from `Q`.  Let `d` be the distance associated with `u`.\n",
            "        *   If `d > dist[u]`, continue to the next iteration (this avoids processing stale entries in the priority queue).\n",
            "\n",
            "        *   If `visited[u]` is true, continue to the next iteration. (already processed this node with shortest distance)\n",
            "        *   Set `visited[u] = true`.  Mark `u` as visited.\n",
            "\n",
            "        *   For each neighbor `v` of vertex `u` (i.e., for each edge `(u, v)` in the graph):\n",
            "            *   Let `weight(u, v)` be the weight of the edge between `u` and `v`.\n",
            "            *   If `dist[u] + weight(u, v) < dist[v]` :\n",
            "                *   `dist[v] = dist[u] + weight(u, v)` (Found a shorter path to `v`)\n",
            "                *   Enqueue `(v, dist[v])` into the priority queue `Q`.\n",
            "\n",
            "3.  **Result:**\n",
            "    *   After the loop finishes, the `dist[v]` array contains the shortest distance from the source node `s` to each vertex `v` in the graph.  If `dist[v]` is still infinity for some vertex `v`, it means there is no path from `s` to `v`.\n",
            "\n",
            "**1.4 Example (Adjacency List Representation):**\n",
            "\n",
            "```python\n",
            "import heapq  # For priority queue implementation\n",
            "\n",
            "def dijkstra(graph, start):\n",
            "    distances = {node: float('inf') for node in graph}\n",
            "    distances[start] = 0\n",
            "    visited = {node: False for node in graph}\n",
            "    pq = [(0, start)]  # (distance, node)\n",
            "\n",
            "    while pq:\n",
            "        dist, u = heapq.heappop(pq)\n",
            "\n",
            "        if dist > distances[u]: # handle duplicates in priority queue\n",
            "          continue\n",
            "\n",
            "        if visited[u]:\n",
            "            continue\n",
            "        visited[u] = True\n",
            "\n",
            "        for v, weight in graph[u].items():  # Iterate through neighbors of u\n",
            "            if distances[u] + weight < distances[v]:\n",
            "                distances[v] = distances[u] + weight\n",
            "                heapq.heappush(pq, (distances[v], v))\n",
            "\n",
            "    return distances\n",
            "\n",
            "# Example Graph (Adjacency List)\n",
            "graph = {\n",
            "    'A': {'B': 4, 'C': 2},\n",
            "    'B': {'A': 4, 'D': 5},\n",
            "    'C': {'A': 2, 'D': 1, 'E': 3},\n",
            "    'D': {'B': 5, 'C': 1, 'E': 1},\n",
            "    'E': {'C': 3, 'D': 1}\n",
            "}\n",
            "\n",
            "start_node = 'A'\n",
            "shortest_distances = dijkstra(graph, start_node)\n",
            "\n",
            "print(f\"Shortest distances from {start_node}: {shortest_distances}\")\n",
            "```\n",
            "\n",
            "**1.5 Time Complexity:**\n",
            "\n",
            "*   Using a binary heap as the priority queue: O((|E| + |V|) log |V|) which simplifies to O(|E| log |V|) in a connected graph.  |V| is the number of vertices, and |E| is the number of edges.\n",
            "*   Using a Fibonacci heap as the priority queue: O(|E| + |V| log |V|)  (Better asymptotic complexity, but more complex to implement)\n",
            "\n",
            "**1.6 Space Complexity:** O(|V|)  (For `dist`, `visited`, and the priority queue).\n",
            "\n",
            "**2. Algorithm: Bellman-Ford Algorithm**\n",
            "\n",
            "Bellman-Ford algorithm finds the shortest paths from a single source node to all other nodes in a *directed* graph, even if the graph contains negative edge weights.  Crucially, it can detect the presence of *negative weight cycles*, which would make finding shortest paths impossible (you could continuously loop through the cycle to decrease the path length indefinitely).\n",
            "\n",
            "**2.1 Input:**\n",
            "\n",
            "*   `Graph G`: A directed weighted graph represented as a list of edges. Each edge has a weight (can be negative).  Edges are represented as (source_node, destination_node, weight).\n",
            "*   `Source Node s`: The starting node for finding the shortest paths.\n",
            "\n",
            "**2.2 Data Structures:**\n",
            "\n",
            "*   `dist[v]`: An array (or dictionary) to store the shortest distance from the source node `s` to each vertex `v`. Initially, set all `dist[v]` to infinity (or a very large number) except for `dist[s]`, which is set to 0.\n",
            "\n",
            "**2.3 Algorithm Steps:**\n",
            "\n",
            "1.  **Initialization:**\n",
            "    *   Set `dist[s] = 0`.\n",
            "    *   For all other vertices `v` (where `v != s`), set `dist[v] = infinity`.\n",
            "\n",
            "2.  **Relaxation:**\n",
            "    *   Repeat the following step |V| - 1 times (where |V| is the number of vertices):\n",
            "        *   For each edge (u, v, weight) in the graph:\n",
            "            *   If `dist[u] + weight < dist[v]` :\n",
            "                *   `dist[v] = dist[u] + weight`\n",
            "\n",
            "3.  **Negative Cycle Detection:**\n",
            "    *   For each edge (u, v, weight) in the graph:\n",
            "        *   If `dist[u] + weight < dist[v]` :\n",
            "            *   There is a negative weight cycle reachable from the source node.  The algorithm cannot find correct shortest paths.  Return an error or indicate that a negative cycle exists.\n",
            "\n",
            "4.  **Result:**\n",
            "    *   If no negative cycle is detected, the `dist[v]` array contains the shortest distances from the source node `s` to each vertex `v` in the graph.\n",
            "\n",
            "**2.4 Example:**\n",
            "\n",
            "```python\n",
            "def bellman_ford(edges, num_vertices, source):\n",
            "    distances = {i: float('inf') for i in range(num_vertices)}\n",
            "    distances[source] = 0\n",
            "\n",
            "    # Relaxation step (V-1 times)\n",
            "    for _ in range(num_vertices - 1):\n",
            "        for u, v, weight in edges:\n",
            "            if distances[u] != float('inf') and distances[u] + weight < distances[v]:\n",
            "                distances[v] = distances[u] + weight\n",
            "\n",
            "    # Negative cycle detection\n",
            "    for u, v, weight in edges:\n",
            "        if distances[u] != float('inf') and distances[u] + weight < distances[v]:\n",
            "            print(\"Graph contains negative weight cycle\")\n",
            "            return None  # Indicate negative cycle\n",
            "\n",
            "    return distances\n",
            "\n",
            "# Example graph with edges as (source, destination, weight)\n",
            "edges = [\n",
            "    (0, 1, -1),\n",
            "    (0, 2, 4),\n",
            "    (1, 2, 3),\n",
            "    (1, 3, 2),\n",
            "    (1, 4, 2),\n",
            "    (3, 2, 5),\n",
            "    (3, 1, 1),\n",
            "    (4, 3, -3)\n",
            "]\n",
            "\n",
            "num_vertices = 5\n",
            "source_node = 0\n",
            "shortest_distances = bellman_ford(edges, num_vertices, source_node)\n",
            "\n",
            "if shortest_distances:\n",
            "    print(f\"Shortest distances from node {source_node}: {shortest_distances}\")\n",
            "```\n",
            "\n",
            "**2.5 Time Complexity:** O(|V| * |E|)\n",
            "\n",
            "**2.6 Space Complexity:** O(|V|)\n",
            "\n",
            "**3. Choosing the Right Algorithm**\n",
            "\n",
            "*   **Dijkstra's:** Use when you have a graph with non-negative edge weights. It's generally faster than Bellman-Ford.\n",
            "*   **Bellman-Ford:** Use when your graph might contain negative edge weights, and you need to detect negative weight cycles.\n",
            "\n",
            "**4. Considerations and Optimizations**\n",
            "\n",
            "*   **Graph Representation:** The choice of adjacency list or adjacency matrix can impact performance, particularly for sparse graphs (adjacency lists are usually better).\n",
            "*   **Priority Queue Implementation:** The priority queue is a crucial component of Dijkstra's.  Using a good implementation (e.g., a binary heap or Fibonacci heap) can significantly improve performance.\n",
            "*   **Directed vs. Undirected Graphs:** Dijkstra's works for both, but Bellman-Ford is designed for directed graphs (although it can be applied to undirected graphs by representing each undirected edge as two directed edges).\n",
            "*   **All-Pairs Shortest Paths:** If you need to find the shortest paths between *all* pairs of vertices, algorithms like Floyd-Warshall may be more efficient than running Dijkstra's or Bellman-Ford multiple times.\n",
            "\n",
            "This detailed explanation and code examples should give you a good foundation for implementing and understanding shortest path algorithms. Remember to consider the specific characteristics of your graph (edge weights, directed/undirected) when choosing the appropriate algorithm.\n",
            "\n",
            "Final Quality: 10.0/10\n",
            "\n",
            "============================================================\n",
            "📊 REFINEMENT HISTORY\n",
            "============================================================\n",
            "Iteration 1: Quality 10.0/10\n",
            "\n",
            "\n",
            "============================================================\n",
            "EXAMPLE 5: Content Summarization\n",
            "============================================================\n",
            "\n",
            "============================================================\n",
            "🔄 Self-Refine Loop\n",
            "============================================================\n",
            "Task: Summarize the key benefits of remote work in 3 concise bullet points\n",
            "\n",
            "────────────────────────────────────────────────────────────\n",
            "ITERATION 1: Initial Generation\n",
            "────────────────────────────────────────────────────────────\n",
            "\n",
            "Generated Output:\n",
            "Here's a 3-point summary of the key benefits of remote work:\n",
            "\n",
            "*   **Increased Flexibility & Work-Life Balance:** Enables employees to manage their schedules and personal responsibilities more effectively.\n",
            "*   **Improved Productivity & Focus:** Reduces commute stress and office distractions, potentially leading to higher output.\n",
            "*   **Cost Savings for Both Employees & Employers:** Lowers expenses related to commuting, office space, and associated overhead.\n",
            "\n",
            "Quality Score: 10.0/10\n",
            "\n",
            "✅ Quality threshold (8.5) reached!\n",
            "============================================================\n",
            "🏆 FINAL OUTPUT (Iteration 2)\n",
            "============================================================\n",
            "Here's a 3-point summary of the key benefits of remote work:\n",
            "\n",
            "*   **Increased Flexibility & Work-Life Balance:** Enables employees to manage their schedules and personal responsibilities more effectively.\n",
            "*   **Improved Productivity & Focus:** Reduces commute stress and office distractions, potentially leading to higher output.\n",
            "*   **Cost Savings for Both Employees & Employers:** Lowers expenses related to commuting, office space, and associated overhead.\n",
            "\n",
            "Final Quality: 10.0/10\n",
            "\n",
            "============================================================\n",
            "📊 REFINEMENT HISTORY\n",
            "============================================================\n",
            "Iteration 1: Quality 10.0/10\n",
            "\n",
            "\n",
            "============================================================\n",
            "EXAMPLE 6: Creative Idea Iteration\n",
            "============================================================\n",
            "\n",
            "============================================================\n",
            "🔄 Self-Refine Loop\n",
            "============================================================\n",
            "Task: Create a tagline for an eco-friendly water bottle brand\n",
            "\n",
            "────────────────────────────────────────────────────────────\n",
            "ITERATION 1: Initial Generation\n",
            "────────────────────────────────────────────────────────────\n",
            "\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "WARNING:tornado.access:429 POST /v1beta/models/gemini-2.0-flash-exp:generateContent?%24alt=json%3Benum-encoding%3Dint (127.0.0.1) 279.39ms\n"
          ]
        },
        {
          "output_type": "error",
          "ename": "TooManyRequests",
          "evalue": "429 POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent?%24alt=json%3Benum-encoding%3Dint: You exceeded your current quota. Please migrate to Gemini 2.0 Flash Preview (Image Generation) (models/gemini-2.0-flash-preview-image-generation) for higher quota limits. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits.",
          "traceback": [
            "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
            "\u001b[0;31mTooManyRequests\u001b[0m                           Traceback (most recent call last)",
            "\u001b[0;32m/tmp/ipython-input-2872872071.py\u001b[0m in \u001b[0;36m<cell line: 0>\u001b[0;34m()\u001b[0m\n\u001b[1;32m     70\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     71\u001b[0m \u001b[0magent6\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mSelfRefineAgent\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 72\u001b[0;31m agent6.self_refine(\n\u001b[0m\u001b[1;32m     73\u001b[0m     \u001b[0;34m\"Create a tagline for an eco-friendly water bottle brand\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     74\u001b[0m     \u001b[0mmax_iterations\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/tmp/ipython-input-2327805013.py\u001b[0m in \u001b[0;36mself_refine\u001b[0;34m(self, task, max_iterations, quality_threshold)\u001b[0m\n\u001b[1;32m     82\u001b[0m         \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"{'─'*60}\\n\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     83\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 84\u001b[0;31m         \u001b[0moutput\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgenerate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtask\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     85\u001b[0m         \u001b[0mquality\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mevaluate_quality\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtask\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moutput\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     86\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/tmp/ipython-input-2327805013.py\u001b[0m in \u001b[0;36mgenerate\u001b[0;34m(self, task)\u001b[0m\n\u001b[1;32m     12\u001b[0m         Response:\"\"\"\n\u001b[1;32m     13\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 14\u001b[0;31m         \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgenerate_content\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprompt\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtext\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     15\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mresponse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstrip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     16\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/generativeai/generative_models.py\u001b[0m in \u001b[0;36mgenerate_content\u001b[0;34m(self, contents, generation_config, safety_settings, stream, tools, tool_config, request_options)\u001b[0m\n\u001b[1;32m    329\u001b[0m                 \u001b[0;32mreturn\u001b[0m \u001b[0mgeneration_types\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mGenerateContentResponse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfrom_iterator\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0miterator\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    330\u001b[0m             \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 331\u001b[0;31m                 response = self._client.generate_content(\n\u001b[0m\u001b[1;32m    332\u001b[0m                     \u001b[0mrequest\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    333\u001b[0m                     \u001b[0;34m**\u001b[0m\u001b[0mrequest_options\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/ai/generativelanguage_v1beta/services/generative_service/client.py\u001b[0m in \u001b[0;36mgenerate_content\u001b[0;34m(self, request, model, contents, retry, timeout, metadata)\u001b[0m\n\u001b[1;32m    833\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    834\u001b[0m         \u001b[0;31m# Send the request.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 835\u001b[0;31m         response = rpc(\n\u001b[0m\u001b[1;32m    836\u001b[0m             \u001b[0mrequest\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    837\u001b[0m             \u001b[0mretry\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mretry\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/api_core/gapic_v1/method.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, timeout, retry, compression, *args, **kwargs)\u001b[0m\n\u001b[1;32m    129\u001b[0m             \u001b[0mkwargs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"compression\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcompression\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    130\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 131\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mwrapped_func\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    132\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    133\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/api_core/retry/retry_unary.py\u001b[0m in \u001b[0;36mretry_wrapped_func\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m    292\u001b[0m                 \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_initial\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_maximum\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmultiplier\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_multiplier\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    293\u001b[0m             )\n\u001b[0;32m--> 294\u001b[0;31m             return retry_target(\n\u001b[0m\u001b[1;32m    295\u001b[0m                 \u001b[0mtarget\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    296\u001b[0m                 \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_predicate\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/api_core/retry/retry_unary.py\u001b[0m in \u001b[0;36mretry_target\u001b[0;34m(target, predicate, sleep_generator, timeout, on_error, exception_factory, **kwargs)\u001b[0m\n\u001b[1;32m    154\u001b[0m         \u001b[0;32mexcept\u001b[0m \u001b[0mException\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mexc\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    155\u001b[0m             \u001b[0;31m# defer to shared logic for handling errors\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 156\u001b[0;31m             next_sleep = _retry_error_helper(\n\u001b[0m\u001b[1;32m    157\u001b[0m                 \u001b[0mexc\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    158\u001b[0m                 \u001b[0mdeadline\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/api_core/retry/retry_base.py\u001b[0m in \u001b[0;36m_retry_error_helper\u001b[0;34m(exc, deadline, sleep_iterator, error_list, predicate_fn, on_error_fn, exc_factory_fn, original_timeout)\u001b[0m\n\u001b[1;32m    212\u001b[0m             \u001b[0moriginal_timeout\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    213\u001b[0m         )\n\u001b[0;32m--> 214\u001b[0;31m         \u001b[0;32mraise\u001b[0m \u001b[0mfinal_exc\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0msource_exc\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    215\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0mon_error_fn\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    216\u001b[0m         \u001b[0mon_error_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mexc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/api_core/retry/retry_unary.py\u001b[0m in \u001b[0;36mretry_target\u001b[0;34m(target, predicate, sleep_generator, timeout, on_error, exception_factory, **kwargs)\u001b[0m\n\u001b[1;32m    145\u001b[0m     \u001b[0;32mwhile\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    146\u001b[0m         \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 147\u001b[0;31m             \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtarget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    148\u001b[0m             \u001b[0;32mif\u001b[0m \u001b[0minspect\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0misawaitable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    149\u001b[0m                 \u001b[0mwarnings\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwarn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_ASYNC_RETRY_WARNING\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/api_core/timeout.py\u001b[0m in \u001b[0;36mfunc_with_timeout\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m    128\u001b[0m                 \u001b[0mkwargs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"timeout\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mremaining_timeout\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    129\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 130\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    131\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    132\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mfunc_with_timeout\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/api_core/grpc_helpers.py\u001b[0m in \u001b[0;36merror_remapped_callable\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m     73\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0merror_remapped_callable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     74\u001b[0m         \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 75\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0mcallable_\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     76\u001b[0m         \u001b[0;32mexcept\u001b[0m \u001b[0mgrpc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mRpcError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mexc\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     77\u001b[0m             \u001b[0;32mraise\u001b[0m \u001b[0mexceptions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfrom_grpc_error\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mexc\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mexc\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.12/dist-packages/google/ai/generativelanguage_v1beta/services/generative_service/transports/rest.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, request, retry, timeout, metadata)\u001b[0m\n\u001b[1;32m   1159\u001b[0m             \u001b[0;31m# subclass.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1160\u001b[0m             \u001b[0;32mif\u001b[0m \u001b[0mresponse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstatus_code\u001b[0m \u001b[0;34m>=\u001b[0m \u001b[0;36m400\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1161\u001b[0;31m                 \u001b[0;32mraise\u001b[0m \u001b[0mcore_exceptions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfrom_http_response\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresponse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   1162\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1163\u001b[0m             \u001b[0;31m# Return the response\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;31mTooManyRequests\u001b[0m: 429 POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent?%24alt=json%3Benum-encoding%3Dint: You exceeded your current quota. Please migrate to Gemini 2.0 Flash Preview (Image Generation) (models/gemini-2.0-flash-preview-image-generation) for higher quota limits. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits."
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [],
      "metadata": {
        "id": "UBFBKvEXRYwd"
      },
      "execution_count": null,
      "outputs": []
    }
  ]
}